﻿using System;
using System.Web.UI;
using System.Text;
using com.calitha.goldparser;
using Texfina.Core.Common;

namespace RH
{
   public class FormulaParser
   {
      private LALRParser _objParser = null;
      private int _intParseErrors = 0;
      private string[] _arrTksExBase = null;
      private FormulaFieldCollection _arrFields = new FormulaFieldCollection();
      private FormulaFieldCollection _arrInUse = new FormulaFieldCollection();

      public FormulaParser(Page p)
      {
         _arrTksExBase = new string[]
                  {
                     "select","from","tableeval",
                     "","+","-","*","/","(",")","=",">","<",">=","<=","and","or","not",
                     "in","case","when","then","else","end",
                     "where","between","isnull"
                  };

         string strFile = p.MapPath(ConstSistema.OTHERFILES_PATH + "/Grammar.cgt");

         CGTReader objReader = new CGTReader(strFile);
         _objParser = objReader.CreateNewParser();
         _intParseErrors = 0;
         _objParser.OnParseError += new LALRParser.ParseErrorHandler(ParseErrorEvent);
      }

      public FormulaFieldCollection Fields
      {
         set { _arrFields = value; }
         get { return _arrFields; }
      }

      public FormulaFieldCollection FieldsUsed
      {
         set { _arrInUse = value; }
         get { return _arrInUse; }
      }

      private void ParseErrorEvent(LALRParser parser, ParseErrorEventArgs args)
      {
         _intParseErrors++;
      }

      public void ParseFormula(string stat)
      {
         ParseFormula(stat, "");
      }

      public void ParseCondition(string stat)
      {

         string strFormula = "";

         strFormula = "SELECT" +
            "\n*" +
            "\nFROM TableEval" +
            "\nWHERE" +
            "\n" + stat;

         this.ParseSQLStat(strFormula);

         //this.LoadExcludeTokens();

         int intNewS = _arrTksExBase.Length;
         //if (_arrTksEx != null)
         //   intNewS = intNewS + _arrTksEx.Length;

         string[] arrExclu = new string[intNewS];

         Array.Copy(_arrTksExBase, 0, arrExclu, 0, _arrTksExBase.Length);
         //if (_arrTksEx != null)
         //   Array.Copy(_arrTksEx, 0, arrExclu, _arrTksExBase.Length, _arrTksEx.Length);

         this.EvalTokensInGroups(stat, arrExclu, "la Condicion");

      }

      public void ParseFormula(string stat, string condition)
      {

         string strFormula = "";

         strFormula = "SELECT" +
            "\n" + stat +
            "\nFROM TableEval" +
            (condition != ""  ? "\nWHERE\n" + condition : "");

         this.ParseSQLStat(strFormula);

         //LoadExcludeTokens();

         int intNewS = _arrTksExBase.Length;
         //if (_arrTksEx != null)
         //   intNewS = intNewS + _arrTksEx.Length;

         string[] arrExclu = new string[intNewS];

         Array.Copy(_arrTksExBase, 0, arrExclu, 0, _arrTksExBase.Length);
         //if (_arrTksEx != null)
         //   Array.Copy(_arrTksEx, 0, arrExclu, _arrTksExBase.Length, _arrTksEx.Length);

         this.EvalTokensInGroups(stat, arrExclu, "la Formula");

         if (condition != "")
            this.EvalTokensInGroups(condition, arrExclu, "la Condicion");

      }

      public void EvalTokensInGroups(string expr, string[] arrayExclu, string msgException)
      {
         string[] strTks = GetEvalTokens(expr);

         foreach (string str in strTks)
         {
            int intPos = Array.IndexOf(arrayExclu, str);
            if (intPos == -1)
            {

               if (str.Substring(0, 1) == "'")
               {
                  if (str.Length > 1)
                     if (str.Substring(str.Length - 1, 1) == "'")
                        continue;
               }

               if (IsNumeric(str))
                  continue;

               bool blnFound = false;

               if (_arrFields.Exists(str))
               {
                  if (!_arrInUse.Exists(str))
                  {
                     FormulaField obj = _arrFields[str];
                     _arrInUse.Add(new FormulaField(obj.Key, obj.Type)); 
                  }
                  blnFound = true;
               }

               if (!blnFound)
               {
                  _arrInUse.Clear();
                  throw new Exception("< " + str + " > no existe o no puede ser usado en " + msgException);
               }
            }
         }

      }

      private string[] GetEvalTokens(string expr)
      {
         string[] strTks = expr.Split(new char[]{' ','+','-','*','/','(',')',
            '>','<','=',',','\r','\n','\t'}, StringSplitOptions.RemoveEmptyEntries);

         return strTks;
      }

      private bool IsNumeric(string s)
      {
         double retNum = 0;

         bool isNum = Double.TryParse(s, System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
         return isNum;
      }

      private void ParseSQLStat(string exprSQL)
      {
         StringBuilder sb = new StringBuilder(exprSQL);

         sb.Replace("{", "");
         sb.Replace("}", "");

         _intParseErrors = 0;
         _objParser.Parse(sb.ToString());
         if (_intParseErrors != 0)
            throw new Exception("Error de Sintaxis en la Formula");
      }

   }
}